6  NumPy 数组运算

6.1 引言向量化运算的威力

NumPy的核心优势在于其强大的向量化运算能力。与Python循环相比,NumPy运算在底层使用优化的C/Fortran代码,可以提供数十倍甚至上百倍的性能提升。在金融数据处理中,这种性能优势使得复杂计算成为可能。

理论背景:SIMD并行计算

NumPy的高性能部分源于SIMD(Single Instruction, Multiple Data)指令集: - 单指令: 对多个数据执行相同的操作 - 多数据: 一次处理多个数值 - 并行执行: CPU的多个ALU同时工作

例如,计算两个数组的和,传统循环需要逐个元素相加,而SIMD可以一次性处理4个或更多元素(取决于CPU架构)。

6.2 统计运算

6.2.1 基本统计量

NumPy提供了丰富的统计函数,这些函数可以直接应用于整个数组或指定轴(axis)。

列表 6.1
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import numpy as np  # 导入NumPy数值计算库
# 已知数据
stock_p=np.array([4.98,5.02,4.95,4.91,4.98,4.92,4.88,4.92,4.88,4.82,4.85,4.89,4.91,4.86,4.84,4.92,5.01,5.04])
# 创建NumPy数组stock_return
stock_return=np.array([-0.7968,1.4141,0.8147,-1.4056,1.2195,0.8197,-0.813,0.8197,1.2448,-0.6186,-0.818,-0.4073,1.0288,0.4132,-1.626,-1.7964,-0.5952,-1.5625])
# 1. 计算股票的最高价并输出
p_max=stock_p.max()
print(f"中国建筑8月最高股价为{p_max}")  # 输出中国建筑8月最高股价为
# 2. 计算股票的平均涨跌幅,使用round()函数保留三位小数并输出
return_avg=stock_return.mean().round(3)
print(f"平均涨跌幅为{return_avg}")  # 输出平均涨跌幅为
# 3. 计算股票涨跌幅的方差和标准差,使用round()函数并保留3位小数
return_var=stock_return.var().round(3)
return_std=stock_return.std().round(3)  # 计算标准差并四舍五入
print(f"涨跌幅的方差{return_var},标准差{return_std}")  # 输出涨跌幅的方差

代码深度解析:

  1. .max().min():

    • 返回数组的最大/最小值
    • 支持axis参数指定沿哪个轴计算
    • 等价于np.max(arr)np.min(arr)
  2. .mean()(算术平均):

    mean = Σx_i / n
    • 受极端值影响
    • 适用于对称分布数据
  3. .var()(方差):

    # 样本方差(默认ddof=0)
    var = Σ(x_i - mean)² / n
    
    # 总体方差(ddof=1)
    var_pop = Σ(x_i - mean)² / (n-1)
    • ddof: Delta Degrees of Freedom(自由度调整)
    • 金融分析常用样本方差(ddof=1)
  4. .std()(标准差):

    std = √variance
    • 与原始数据同单位,更易解释
    • 金融含义: 波动率的度量,风险的核心指标

6.2.2 多维数组的统计运算

列表 6.2
# =============================================================================
# 题目:沿指定轴计算统计量
# =============================================================================
# 本任务演示如何对多维数组沿指定轴进行统计运算
# 场景:分析多只股票多日的收益率数据,计算不同维度的统计量

# ==================== 创建多维数据 ====================
# 多只股票多日收益率:3只股票 × 4个交易日的收益率矩阵
# 每行代表一只股票,每列代表一个交易日
returns = np.array([
    [0.05, 0.03, -0.02, 0.04],  # 股票A:4个交易日的收益率
    [0.02, 0.01, 0.03, -0.01],  # 股票B:4个交易日的收益率
    [-0.01, 0.02, 0.01, 0.05]   # 股票C:4个交易日的收益率
])  # 形状为(3, 4)的二维数组,3行4列

# ==================== 沿axis=0计算每日的平均收益率 ====================
# axis=0:沿垂直方向(跨行)计算,即对每列计算均值
# 这里跨3只股票计算每个交易日的市场平均收益率
daily_avg = returns.mean(axis=0)  # 对每列求均值,返回长度为4的一维数组
print(f"每日平均收益率: {daily_avg}")
# 输出解读:返回4个值,分别对应4个交易日的市场平均收益率
# 金融含义:市场平均收益率反映整体市场的表现,可用于计算市场因子
# 应用:如果某日市场平均收益率为正,说明当日市场整体上涨

# ==================== 沿axis=1计算每只股票的平均收益率 ====================
# axis=1:沿水平方向(跨列)计算,即对每行计算均值
# 这里跨4个交易日计算每只股票的平均收益率
stock_avg = returns.mean(axis=1)  # 对每行求均值,返回长度为3的一维数组
print(f"每只股票平均收益率: {stock_avg}")
# 输出解读:返回3个值,分别对应股票A、B、C的平均收益率
# 金融含义:个股平均收益率反映该股票在统计期内的典型表现
# 应用:比较不同股票的平均收益率,筛选表现较好的股票

# ==================== 计算累积收益率 ====================
# .cumsum(axis=1):沿axis=1方向计算累积和
# 累积和用于计算多期收益率的累积效果
cumulative_returns = returns.cumsum(axis=1)  # 对每行计算累积和,返回形状为(3, 4)的数组
print(f"\n累积收益率:\n{cumulative_returns}")
# 输出解读:每行的第i个值 = 该行前i个元素的和
# 例如股票A的第3个值 = 0.05 + 0.03 + (-0.02) = 0.06,表示前3日累积收益率为6%
# 金融含义:累积收益率反映投资一段时间的总收益
# 应用:计算持仓的累积收益,评估投资策略的历史表现

axis参数理解: - axis=0: 垂直方向(跨行),对每列计算 - axis=1: 水平方向(跨列),对每行计算 - axis=None: 对所有元素计算(默认)

金融应用: - axis=0: 多只股票同一时间的表现 - axis=1: 单只股票的时间序列特征

6.3 数学运算

6.3.1 算术运算

NumPy支持元素级的算术运算,这是向量化的核心。

列表 6.3
# =============================================================================
# 题目:NumPy数组的算术运算
# =============================================================================
# 本任务演示NumPy数组的元素级算术运算
# 场景:金融场景中的价格调整、价差计算、持仓价值计算、收益率计算

# ==================== 创建基础数据 ====================
# 基础算术:创建股价序列
prices = np.array([10, 20, 30, 40, 50])  # 5个交易日的股价数据,单位:元

# ==================== 加法:价格调整 ====================
# 创建调整数组,模拟价格调整(如分红、拆股等)
adjustment = np.array([1, 2, 3, 4, 5])  # 每天的价格调整幅度
new_prices = prices + adjustment  # 对应元素相加,得到调整后的价格
print(f"调整后价格: {new_prices}")
# 输出解读:[11, 22, 33, 44, 55],每个元素 = 对应位置的price + adjustment
# 金融应用:股票除权除息后的价格调整,或者计算复权价格

# ==================== 减法:价差计算 ====================
# prices[1:]:从索引1开始到最后的切片,即[20, 30, 40, 50]
# prices[:-1]:从开始到倒数第二个的切片,即[10, 20, 30, 40]
# 相减得到相邻两天的价格差
price_diff = prices[1:] - prices[:-1]  # 计算相邻价格的差值
print(f"价格变化: {price_diff}")
# 输出解读:[10, 10, 10, 10],表示每天价格上涨10元
# 金融含义:价格差反映价格的绝对变化,用于计算价格动量
# 应用:技术分析中的价格变化率、MACD指标等

# ==================== 乘法:计算持仓价值 ====================
# 创建持仓数量数组
shares = np.array([100, 200, 150])  # 3只股票的持仓数量
# prices[:3]:取prices的前3个元素[10, 20, 30]
position_value = prices[:3] * shares  # 对应元素相乘,计算每只股票的持仓价值
print(f"\n持仓价值: {position_value}")
# 输出解读:[1000, 4000, 4500],分别表示3只股票的持仓价值
# 金融应用:持仓价值 = 股价 × 持仓数量,用于计算投资组合的市值
# 注意:两个数组形状必须相同,或可以广播

# ==================== 除法:计算收益率 ====================
# 简单收益率公式:return = (P_t - P_{t-1}) / P_{t-1}
# prices[1:]:当期价格,prices[:-1]:上期价格
returns = (prices[1:] - prices[:-1]) / prices[:-1]  # 计算日收益率
print(f"\n简单收益率: {returns}")
# 输出解读:[1.0, 0.5, 0.33, 0.25],分别表示每天的收益率(小数形式)
# 金融含义:第1天收益率 = (20-10)/10 = 100%,第2天 = (30-20)/20 = 50%
# 应用:计算历史收益率,用于评估股票的历史表现和风险

# ==================== 幂运算:复利计算 ====================
# np.arange(1, 6):生成整数序列[1, 2, 3, 4, 5],代表年数
# (1 + 0.05):1 + 年化收益率5%
# **:幂运算符,计算复利因子
compound = (1 + 0.05) ** np.arange(1, 6)  # 计算1到5年的复利因子
print(f"\n5年复利因子: {compound}")
# 输出解读:[1.05, 1.1025, 1.1576, 1.2155, 1.2763]
# 金融含义:第1年末本金变为1.05倍,第2年末变为1.1025倍,依此类推
# 应用:复利计算、未来价值计算、投资回报预测

运算符对应关系:

运算符 NumPy函数 描述
+ np.add 加法
- np.subtract 减法
* np.multiply 乘法
/ np.divide 除法
// np.floor_divide 整除
% np.mod 取模
** np.power 幂运算

6.3.2 数学函数

NumPy提供了完整的数学函数库,这些函数都是向量化的。

列表 6.4
# =============================================================================
# 题目:NumPy数学函数在金融计算中的应用
# =============================================================================
# 本任务演示NumPy常用数学函数在金融场景中的应用
# 场景:对数收益率计算、复利计算、波动率计算、价格取整

# ==================== 创建示例数据 ====================
# 示例数据:5个交易日的股价数据
prices = np.array([100, 105, 98, 102, 110])  # 股价序列,单位:元

# ==================== 1. 对数收益率计算(金融常用) ====================
# np.log(prices):计算价格的自然对数,ln(prices)
# np.diff():计算相邻元素的差值,a[n] - a[n-1]
# 对数收益率公式:r_log = ln(P_t) - ln(P_{t-1}) = ln(P_t / P_{t-1})
log_returns = np.diff(np.log(prices))  # 计算对数收益率
print(f"对数收益率: {log_returns}")
# 输出解读:返回4个值,表示4个交易日的对数收益率
# 金融含义:对数收益率具有时间可加性,多期收益率可以直接相加
# 优势:相比简单收益率,对数收益率更接近正态分布,便于统计建模
# 应用:连续复利模型、Black-Scholes期权定价模型

# ==================== 2. 指数函数:复利计算 ====================
# 创建年化收益率数组:4只股票的年收益率
annual_returns = np.array([0.05, 0.06, 0.04, 0.07])  # 小数形式,5%、6%、4%、7%
# annual_returns * 5:计算5年的累积收益率
# np.exp():指数函数,e^x,用于连续复利计算
# cumulative_5yr = e^(r * 5) - 1,连续复利公式
cumulative_5yr = np.exp(annual_returns * 5) - 1  # 计算5年累计收益率
print(f"\n5年累计收益率: {cumulative_5yr}")
# 输出解读:返回4个值,表示4只股票5年后的累计收益率
# 金融含义:假设年化收益率为5%,5年后累计收益率 = e^(0.05*5) - 1 = 28.4%
# 应用:长期投资回报预测、养老金计算、财富增长规划

# ==================== 3. 平方和平方根:波动率计算 ====================
# np.std():计算标准差,衡量收益率波动程度
volatility = np.std(annual_returns)  # 计算年化收益率的标准差
# ** 2:平方运算,计算方差
variance = volatility ** 2  # 方差 = 标准差的平方
print(f"\n波动率: {volatility:.4f}")
print(f"方差: {variance:.4f}")
# 输出解读:波动率如0.0112表示年收益率的标准差为1.12个百分点
# 金融含义:波动率是风险的量化度量,波动率越大,风险越高
# 应用:投资组合风险管理、VaR(在险价值)计算、期权定价

# ==================== 4. 绝对值:计算收益的绝对变化 ====================
# np.diff(prices):计算相邻价格的差值
# np.abs():计算绝对值,将负数转为正数
abs_changes = np.abs(np.diff(prices))  # 计算价格变化的绝对值
print(f"\n价格绝对变化: {abs_changes}")
# 输出解读:返回正数,表示价格变化的幅度(不考虑方向)
# 金融应用:计算价格波动幅度,不考虑涨跌方向
# 区别:price_diff有正负(方向),abs_changes只有大小(幅度)

# ==================== 5. 取整函数 ====================
# 创建浮点数价格数组,模拟包含小数的股价
prices_float = np.array([100.56, 105.23, 98.87])  # 股价保留两位小数

# np.floor():向下取整,返回不大于x的最大整数
print(f"\n向下取整: {np.floor(prices_float)}")
# 输出解读:[100., 105., 98.],每个元素向下取整
# 应用:金融计算中需要保守估计时使用向下取整

# np.ceil():向上取整,返回不小于x的最小整数
print(f"向上取整: {np.ceil(prices_float)}")
# 输出解读:[101., 106., 99.],每个元素向上取整
# 应用:计算所需资金时向上取整,确保资金充足

# np.round():四舍五入到最接近的整数
print(f"四舍五入: {np.round(prices_float)}")
# 输出解读:[101., 105., 99.],按照四舍五入规则取整
# 应用:报价、显示价格时使用四舍五入
# 注意:round(0.5) = 0,采用"银行家舍入法"(向偶数舍入)

补充说明:对数收益率的数学基础

在金融中,对数收益率(Log Return)优于简单收益率:

\[ r_{log} = \ln\left(\frac{P_t}{P_{t-1}}\right) = \ln(P_t) - \ln(P_{t-1}) \]

优势: 1. 时间可加性: 多期收益率可以直接相加 \[ r_{log,1→3} = r_{log,1→2} + r_{log,2→3} \] 2. 对称性: 正负收益对称(-50%和+50%的对数收益绝对值相等) 3. 正态性: 更接近正态分布,便于统计推断

6.4 金融指标计算实例

6.4.1 案例股票风险收益分析

列表 6.5
# =============================================================================
# 题目:综合运用NumPy进行股票风险收益分析
# =============================================================================
# 本任务综合运用NumPy的统计和运算函数,对股票进行全面的风险收益分析
# 场景:分析中国建筑某月的股价数据,计算各类风险和收益指标

# ==================== 创建股价数据 ====================
# 中国建筑8月股价数据(单位:元),共18个交易日
stock_p = np.array([4.98, 5.02, 4.95, 4.91, 4.98, 4.92, 4.88, 4.92,
                    4.88, 4.82, 4.85, 4.89, 4.91, 4.86, 4.84, 4.92,
                    5.01, 5.04])  # 一维数组,存储每日收盘价

# ==================== 计算日收益率 ====================
# stock_p[1:]:从第2个价格开始(第2天到第18天的价格)
# stock_p[:-1]:从第1个价格到倒数第2个(第1天到第17天的价格)
# 收益率公式:(当期价格 - 上期价格) / 上期价格
daily_returns = (stock_p[1:] - stock_p[:-1]) / stock_p[:-1]  # 计算17个日收益率
# 输出解读:返回长度为17的数组,表示第2天到第18天的日收益率
# 金融含义:正收益率表示当天上涨,负收益率表示当天下跌

# ==================== 计算基本统计量 ====================
# .mean():计算日平均收益率
mean_return = daily_returns.mean()  # 17个日收益率的算术平均
# 金融含义:表示平均每日的收益率水平,用于估计未来预期收益

# .std():计算日收益率的标准差
std_return = daily_returns.std()  # 日波动率
# 金融含义:表示日收益率围绕均值波动的程度,是日风险的度量

# 年化波动率:将日波动率转换为年化波动率
# np.sqrt(252):根号252,252是一年的交易日数
# 公式:年化波动率 = 日波动率 × sqrt(252)
annualized_vol = std_return * np.sqrt(252)  # 计算年化波动率
# 金融含义:假设日波动率持续一年,年化波动率约为多少
# 应用:不同资产的波动率比较、风险预算、风险控制

# ==================== 打印风险收益分析报告 ====================
print("=" * 50)  # 打印分隔线
print("中国建筑8月风险收益分析")  # 报告标题
print("=" * 50)  # 打印分隔线

# 打印收益率统计信息
print(f"\n【收益率统计】")
print(f"  日平均收益率: {mean_return:.4%}")  # 格式化为百分比,保留4位小数
print(f"  日收益率标准差: {std_return:.4%}")  # 日波动率
print(f"  年化波动率: {annualized_vol:.4%}")  # 年化波动率
# 输出解读:如日平均收益率-0.10%表示平均每日下跌0.10%
# 金融含义:负的平均收益率表明该月整体表现不佳

# ==================== 计算价格统计 ====================
# .max():计算最高价
max_price = stock_p.max()  # 8月期间的最高股价
# .min():计算最低价
min_price = stock_p.min()  # 8月期间的最低股价
# 价格区间:最高价 - 最低价
price_range = max_price - min_price  # 计算8月价格波动区间

print(f"\n【价格统计】")
print(f"  最高价: {max_price:.2f}元")  # 保留2位小数
print(f"  最低价: {min_price:.2f}元")  # 保留2位小数
print(f"  价格区间: {price_range:.2f}元")  # 价格波动幅度
# 金融应用:根据价格区间设置止损止盈位,如最低价附近设置止损

# ==================== 计算下行风险指标 ====================
# 布尔索引:筛选收益率为负的交易日
daily_returns < 0  # 返回布尔数组,True表示该日收益率为负
downside_returns = daily_returns[daily_returns < 0]  # 提取所有负收益率
# 金融含义:下跌日的收益率数据,用于计算下行风险

# 下行波动率:只考虑下跌日的波动率
downside_risk = downside_returns.std() * np.sqrt(252)  # 年化下行波动率
# 金融含义:下行风险只关注下跌时的波动,更符合投资者对风险的感知

print(f"\n【风险指标】")
print(f"  下跌日波动率: {downside_risk:.4%}")  # 下行风险
# 输出解读:下行风险通常小于整体波动率,因为忽略了上涨日的波动

# 夏普比率:衡量单位风险的超额收益
# 公式:夏普比率 = (年化收益率 - 无风险利率) / 年化波动率
# mean_return * 252:日收益率年化
# 0.03:假设无风险利率为3%
sharpe_ratio = (mean_return * 252 - 0.03) / annualized_vol
print(f"  夏普比率(假设无风险利率3%): {sharpe_ratio:.2f}")
# 输出解读:夏普比率如-0.5,表示每承担1单位风险,超额收益为-0.5%
# 金融含义:夏普比率 > 1 为优秀,< 1 为一般,< 0 为较差(负值表示回报低于无风险利率)
# 应用:比较不同投资策略的风险调整后收益,筛选性价比高的投资

6.5 集合运算

列表 6.6
# =============================================================================
# 题目:NumPy集合运算在股票筛选中的应用
# =============================================================================
# 本任务演示NumPy的集合运算函数
# 场景:分析两个投资组合的持仓差异,找出共同持仓和独有持仓

# ==================== 创建投资组合数据 ====================
# 投资组合A的股票代码:包含4只股票
portfolio_a = np.array(['600519.SH', '000858.SZ', '600036.SH', '000002.SZ'])
# 投资组合B的股票代码:包含4只股票
portfolio_b = np.array(['600036.SH', '601318.SH', '000858.SZ', '600000.SH'])
# 注意:股票代码格式,上海交易所.SH,深圳交易所.SZ

# ==================== 交集:找出共同持仓 ====================
# np.intersect1d():计算两个数组的交集,返回共有的元素(已排序)
common = np.intersect1d(portfolio_a, portfolio_b)  # 找出A和B都有的股票
print(f"共同持仓: {common}")
# 输出解读:['000858.SZ' '600036.SH'],两只组合共同持有这两只股票
# 金融应用:投资组合重叠度分析,避免过度集中风险

# ==================== 并集:找出所有股票 ====================
# np.union1d():计算两个数组的并集,返回所有不重复的元素(已排序)
all_stocks = np.union1d(portfolio_a, portfolio_b)  # 合并A和B的所有股票,去重
print(f"所有股票: {all_stocks}")
# 输出解读:返回6只股票(4+4-2=6),因为2只是共同的
# 金融应用:构建分散化的投资池,确保股票多样性

# ==================== 差集:找出独有持仓 ====================
# np.setdiff1d(ar1, ar2):计算在ar1中但不在ar2中的元素
# 找出A组合独有(在A中但不在B中)的股票
unique_a = np.setdiff1d(portfolio_a, portfolio_b)  # A特有的股票
print(f"A组合独有: {unique_a}")
# 输出解读:['000002.SZ' '600519.SH'],这两只只在A组合中

# 找出B组合独有(在B中但不在A中)的股票
unique_b = np.setdiff1d(portfolio_b, portfolio_a)  # B特有的股票
print(f"B组合独有: {unique_b}")
# 输出解读:['600000.SH' '601318.SH'],这两只只在B组合中
# 金融应用:策略差异分析,了解不同投资经理的选股偏好

6.6 比较运算与布尔索引

列表 6.7
# =============================================================================
# 题目:NumPy比较运算与条件筛选
# =============================================================================
# 本任务演示NumPy的比较运算和布尔索引功能
# 场景:根据股价条件筛选股票,找出符合特定标准的股票

# ==================== 创建股价数据 ====================
# 股价数据:5只股票的最新价格
prices = np.array([10.5, 20.3, 15.8, 25.2, 18.6])  # 单位:元

# ==================== 比较运算 ====================
# >:比较运算符,返回布尔数组(True/False)
# 比较每个元素是否大于20
above_20 = prices > 20  # 返回[False, False, False, True, False]
print(f"价格>20元: {above_20}")
# 输出解读:只有第4只股票(25.2元)的价格大于20元,对应位置为True
# 金融应用:筛选高价股、低价股,设置价格阈值

# ==================== 布尔索引 ====================
# 布尔索引:用布尔数组作为索引,提取True位置的元素
# prices > 20:返回布尔数组[False, False, False, True, False]
# prices[布尔数组]:只保留True位置的元素
expensive_stocks = prices[prices > 20]  # 筛选价格大于20的股票
print(f"高价股票: {expensive_stocks}")
# 输出解读:[25.2],只有1只股票价格大于20元
# 金融应用:从股票池中筛选符合条件的股票,如PE < 20、市值 > 100亿

# ==================== 多条件筛选 ====================
# &:逻辑与运算符,两个条件同时满足时为True
# (prices >= 15):价格大于等于15
# (prices <= 25):价格小于等于25
# 两个条件用括号括起来,因为&的优先级高于比较运算符
filtered = prices[(prices >= 15) & (prices <= 25)]  # 筛选价格在15-25之间的股票
print(f"价格在15-25元之间: {filtered}")
# 输出解读:[15.8, 20.3, 18.6],3只股票的价格在15-25元之间
# 金融含义:价格区间筛选,寻找合理估值的股票
# 应用:价值投资策略,筛选股价处于合理区间的股票

# ==================== 逻辑运算符说明 ====================
# &:逻辑与(AND),两个条件都为True时结果为True
# |:逻辑或(OR),至少一个条件为True时结果为True
# ~:逻辑非(NOT),True变False,False变True
# 注意:必须用括号,因为位运算符优先级高于比较运算符
# 例如:prices > 15 & prices < 25 会报错,应写成 (prices > 15) & (prices < 25)

6.7 排序与搜索

6.8 性能优化建议

列表 6.9
# =============================================================================
# 题目:NumPy性能优化最佳实践
# =============================================================================
# 本任务演示NumPy性能优化的关键技巧
# 场景:对比Python循环和NumPy向量化的性能差异,展示优化方法

# ==================== 导入必要的库 ====================
import time  # 导入time模块,用于计时

# ==================== 创建大规模测试数据 ====================
n = 1000000  # 数据规模:100万元素
arr1 = np.random.randn(n)  # 生成100万个标准正态分布随机数
arr2 = np.random.randn(n)  # 再生成100万个标准正态分布随机数
# np.random.randn(n):生成从标准正态分布(均值0,标准差1)中抽取的n个随机数

# ==================== 1. 使用NumPy内置函数而非Python循环 ====================
# 慢速方法:使用Python循环逐个元素相加
start = time.time()  # 记录开始时间
result_slow = []  # 创建空列表存储结果
for i in range(n):  # 循环100万次
    result_slow.append(arr1[i] + arr2[i])  # 逐个元素相加并添加到列表
slow_time = time.time() - start  # 计算耗时(当前时间 - 开始时间)

# 快速方法:使用NumPy向量化运算
start = time.time()  # 记录开始时间
result_fast = arr1 + arr2  # 直接对两个数组相加,一次性完成所有运算
fast_time = time.time() - start  # 计算耗时

# ==================== 打印性能对比结果 ====================
print(f"循环时间: {slow_time:.4f}秒")  # 显示循环耗时
print(f"向量化时间: {fast_time:.4f}秒")  # 显示向量化耗时
print(f"加速比: {slow_time/fast_time:.1f}倍")  # 计算加速比(循环时间 / 向量化时间)
# 输出解读:加速比通常在50-200倍之间,取决于CPU和数据规模
# 性能优势原因:
# 1. NumPy底层使用C语言,避免了Python解释器开销
# 2. SIMD指令集并行处理多个数据
# 3. 连续内存访问,提高缓存命中率
# 金融应用:大规模数据处理(如tick数据、高频交易数据)必须使用向量化

# ==================== 2. 就地操作节省内存 ====================
arr_large = np.random.randn(10000)  # 创建包含10000个元素的数组

# 慢速方法:创建新数组(需要额外内存分配)
result1 = arr_large * 2  # 创建一个新数组存储结果,原数组不变
# 内存消耗:原数组(80KB) + 新数组(80KB) = 160KB

# 快速方法:原地修改(不需要额外内存)
arr_large *= 2  # 使用*=运算符,直接在原数组上修改,不创建新数组
# 内存消耗:原数组(80KB),节省50%内存
# 性能优势:避免内存分配和复制,速度更快
# 注意:就地操作会修改原数组,如果需要保留原数据应先备份

# ==================== 3. 预分配数组 ====================
# 慢速方法:动态增长列表(频繁内存分配)
result = []  # 创建空列表
for i in range(10000):  # 循环10000次
    result.append(i ** 2)  # 每次追加元素,可能触发内存重新分配
result = np.array(result)  # 最后转换为NumPy数组
# 性能问题:
# 1. 列表append可能需要多次内存重新分配
# 2. 最终转换需要复制所有数据

# 快速方法:预分配数组(一次性内存分配)
result = np.empty(10000)  # 预先分配10000个元素的空数组(未初始化)
for i in range(10000):  # 循环10000次
    result[i] = i ** 2  # 直接填充预分配的数组,无需重新分配内存
# 性能优势:
# 1. 一次性分配所有内存,避免多次分配
# 2. 直接写入数组,无需列表到数组的转换
# 金融应用:生成大规模模拟数据、历史数据回测